/*	--- 搜索和替换 --- 

----------------------------------

#	分割字符串

使用正则表达式分割字符串可以实现更加灵活的功能。String.split()方法传入的正是正则表达式。我们来看下面的代码：
	"a b c".split("\\s"); 	// { "a", "b", "c" }
	"a b  c".split("\\s"); 	// { "a", "b", "", "c" }
	"a, b ;; c".split("[\\,\\;\\s]+"); 	// { "a", "b", "c" }


如果我们想让用户输入一组标签，然后把标签提取出来，因为用户的输入往往是不规范的，这时，使用合适的正则表达式，就可以消除多个空格、混合,和;这些不规范的输入，直接提取出规范的字符串。

--------------------------


#	搜索字符串

使用正则表达式还可以搜索字符串，我们来看例子：	*/
import java.util.regex.*;

public class Main {
	public static void main(String[] args) {
		String s = "the quick brown fox jumps over the lazy dog.";
		
		Pattern p = Pattern.compile("\\wo\\w");
		Matcher m = p.matcher(s);

		while (m.find()) { // m.find()进行自动顺序匹配
			String sub = s.substring(m.start(), m.end()); // (m.start(),m.end())是'上一次m.find()'到'这一次m.find()'
			System.out.println(sub);
		} // fox dog

	}
}

/*
我们获取到Matcher对象后，不需要调用matches()方法（因为匹配整个串肯定返回false）

而是反复调用Matcher.find()方法，在整个串中搜索能匹配上\\wo\\w规则的子串，并打印出来。

这种方式比String.indexOf()要灵活得多，因为我们搜索的规则是3个字符：中间必须是o，前后两个必须是字符[A-Za-z0-9_]。



-------------------------------------


#	替换字符串

使用正则表达式替换字符串可以直接调用String.replaceAll()，它的第一个参数是正则表达式，第二个参数是待替换的字符串。我们还是来看例子：	*/
import java.util.regex.*;

public class Main {
	public static void main(String[] args) {
		String s = "The     quick\t\t brown   fox  jumps   over the  lazy dog.";
		String r = s.replaceAll("\\s+", " ");

		System.out.println(r); // "The quick brown fox jumps over the lazy dog."

	}
}

/*
上面的代码把不规范的连续空格分隔的句子变成了规范的句子。可见，灵活使用正则表达式可以大大降低代码量。


-----------------------------------


#	反向引用

如果我们要把搜索到的指定字符串按规则替换，比如前后各加一个<b>xxxx</b>，这个时候，使用replaceAll()的时候，我们传入的第二个参数可以使用$1、$2来反向引用匹配到的子串。例如：	*/
import java.util.regex.*;

public class Main {
	public static void main(String[] args) {
		String s = "the quick brown fox jumps over the lazy dog.";
		String r = s.replaceAll("\\s([a-z]{4})\\s", " <b>$1</b>");
			// $1 代表 String.replaceAll(p1, p2); 中的p1(第一个参数)

		System.out.println(r);

	}
}

/*
上述代码的运行结果是：

	the quick brown fox jumps <b>over</b> the <b>lazy</b> dog.

它实际上把任何4字符单词的前后用<b>xxxx</b>括起来。实现替换的关键就在于" <b>$1</b> "，它用匹配的分组子串([a-z]{4})替换了$1。


*/



/*	---- 练习 ---- 

模板引擎是指，定义一个字符串作为模板：

	Hello, ${name}! You are learning ${lang}!

其中，以${key}表示的是变量，也就是将要被替换的内容

当传入一个Map<String, String>给模板后，需要把对应的key替换为Map的value。

例如，传入Map为：

	{
	    "name": "Bob",
	    "lang": "Java"
	}

然后，${name}被替换为Map对应的值"Bob”，${lang}被替换为Map对应的值"Java"，最终输出的结果为：

	Hello, Bob! You are learning Java!

请编写一个简单的模板引擎，利用正则表达式实现这个功能。

提示：参考Matcher.appendReplacement()方法。


*/
import java.util.HashMap;
import java.util.regex.*;

public class Template {
	HashMap<String, String> map = new HashMap<String, String>();

	map.put("name", "Bob");
	map.put("lang", "java");

	String s = "Hello, ${name}! You are learning ${lang}!";

	Pattern p = Pattern.compile("\\$\\{(\\w+)}");
	Matcher m = p.matcher(s);
	StringBuilder sb = new StringBuilder();

	while (m.find()) {
		String tmp = s.substring(m.start()+2, m.end()-1 ); // 截取m.find()两次之间的length
		m.appendReplacement(sb, map.get(tmp)); 
		//把匹配到的内容替换为map.get(tmp),并添加到sb中.
		//并且把从上次替换的位置到这次替换位置之间的字符串也拿到,然后，加上这次替换后的结果一起追加到StringBuffer里（假如这次替换是第一次替换，那就是只追加替换后的字符串啦）。
	}

	m.appendTail(sb); // 把最后一次匹配到的内容之后的字符串追加到StringBuilder中。
	System.out.println(sb.toString());

}



//方法2
public class FindReplaceProblem {
    public static void main(String[] args) {
        String s = "Hello, ${name}! You are learning ${lang}!";

        Template template = new Template(s);
        Map<String, String> map = new HashMap<>();

        map.put("name", "Bob");
        map.put("lang", "Java");

        System.out.println(template.render(map));
    }
}

class Template {
    private final String template;
    private final Pattern pattern = Pattern.compile("\\$\\{(\\w+)}");

    public Template(String template) {
        this.template = template;
    }

    public String render( Map<String, String> data ) {
        Matcher m = pattern.matcher(template);
        StringBuffer sb = new StringBuffer();

        while (m.find()) {
            String key = template.substring(m.start() + 2, m.end() - 1); // m.start()+2 -> 跳过$
            //因为m.find()匹配"\\$\\{(\\w+)}",记录遇到$的前一个index;同时m.end()记录的是匹配中断的\s,所以需要m.end()-1
            m.appendReplacement(sb, data.get(key)); // m添加m.start()到m.end()的序列到sb中,替换掉m.find()为data.get(key)
        }

        m.appendTail(sb);
        return sb.toString();
    }
}


/*	--- 搜索和替换 の 小结 --- 

1. 使用正则表达式可以：
	分割字符串：String.split()
	搜索子串：Matcher.find()
	替换字符串：String.replaceAll()


2. 使用$1,$2替换'上一个传入的参数,以()划分num'


3. 模板引擎: Matcher.appendReplacement()方法

	定义一个字符串作为模板
		Hello, ${name}! You are learning ${lang}!
	
	以${key}表示的是变量，也就是将要被替换的内容

	传入一个Map<String, String>给模板后，需要把对应的key替换为Map的value。



*/



// 练习 的 样板例子
 Pattern p = Pattern.compile("cat");
 Matcher m = p.matcher("one cat two cats in the yard");
 StringBuilder sb = new StringBuilder();

 while (m.find()) { // 因为m.find()导致了读入的是String.subString()
     m.appendReplacement(sb, "dog"); // <- subString传入
 }

 m.appendTail(sb);
 System.out.println(sb.toString()); // one dog two dogs in the yard


/*

appendReplacement(StringBuilder sb, String replacement):

	It reads characters from the input sequence

	Parameters:
		sb - The target string builder
		replacement - The replacement string

	Returns:
		This matcher

	Throws:
		IllegalStateException - If no match has yet been attempted, or if the previous match operation failed
	
		IllegalArgumentException - If the replacement string refers to a named-capturing group that does not exist in the pattern
	
		IndexOutOfBoundsException - If the replacement string refers to a capturing group that does not exist in the pattern

	Since:
		9

-------------------------------

appendTail():
	public StringBuffer appendTail​(StringBuffer sb)

	Implements a terminal append-and-replace step.

	This method reads characters from the input sequence, starting at the append position, and appends them to the given string buffer. It is intended to be invoked after one or more invocations of the appendReplacement method in order to copy the remainder of the input sequence.

	Parameters:
	sb - The target string buffer

	Returns:
	The target string buffer

*/








import java.util.regex.Matcher; 
import java.util.regex.Pattern;

public class Text { 
	public static void main (String [] args) { 
		Pattern p = Pattern.compile ("b"); 
		Matcher m = p.matcher("hi buddy! How is bill?"); 

		while (m.find()) { 
			System.out.println ("Match started at:"+ m.start(0)); 
			System.out.println ("Match ended at:"+m.end(0)); 
		} 
	} 
}
/*
运行结果：

Match started at:3
Match ended at:4

Match started at:17
Match ended at:18

*/










